import lombok.extern.slf4j.Slf4j;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;

/**
 * <p>
 * 交易
 * </p>
 *
 * @author KANLEI
 * @email freefree.me@qq.com
 * @create 2020-10-29
 */
@Slf4j
public class Transaction {
    // hash of transaction
    public String transactionId;

    public PublicKey sender;

    public PublicKey receiver;

    public double value;
    // only signature
    public byte[] signature;

    public ArrayList<TransactionInput> txInputs = new ArrayList<>();

    public ArrayList<TransactionOutput> txOutputs = new ArrayList<>();

    private static int sequence = 0;

    public Transaction(PublicKey from, PublicKey to, double value, ArrayList<TransactionInput> txInputs) {

        this.sender = from;
        this.receiver = to;
        this.value = value;
        this.txInputs = txInputs;
    }
    // 计算SHA-256
    private String calculate() {
        sequence++;
        return StringUtils.applySHA256(
                StringUtils.getStringFromKey(sender) +
                        StringUtils.getStringFromKey(receiver) +
                        value +
                        sequence
        );
    }

    // 生成唯一签名
    public void generateSignature(PrivateKey key) {
        String data = StringUtils.getStringFromKey(sender) + StringUtils.getStringFromKey(receiver) + value;
        signature = StringUtils.applyECDSASig(key, data);
    }

    // 校验数据是否被篡改了
    public boolean verify() {
        String data = StringUtils.getStringFromKey(sender) + StringUtils.getStringFromKey(receiver) + value;
        return StringUtils.verifyECDSASig(sender, data, signature);
    }

    // 处理交易
    public boolean processTransaction() {

        if (!verify()) {
            log.error("Transaction signature verify is failure.");
            return false;
        }
        // 确保没有被消费
        for (TransactionInput txInput : txInputs) {
            txInput.UTXO = FreeChain.UTXOs.get(txInput.transactionOutputId);
        }

        double leftOver = getInputValue() - value;
        // transaction of hash
        transactionId = calculate();
        txOutputs.add(new TransactionOutput(receiver, value, transactionId));
        txOutputs.add(new TransactionOutput(sender, leftOver, transactionId));
        // 添加新的交易记录
        for (TransactionOutput txOutput : txOutputs) {
            FreeChain.UTXOs.put(txOutput.id, txOutput);
        }
        // 移除之前的数据
        for (TransactionInput txInput : txInputs) {
            if (txInput.UTXO == null) continue;
            FreeChain.UTXOs.remove(txInput.UTXO.id);
        }

        return true;
    }

    public double getInputValue() {
        double total = 0;
        for (TransactionInput txInput : txInputs) {
            if (txInput.UTXO == null) continue;
            total += txInput.UTXO.value;
        }
        return total;
    }

    public double getOutputValue() {
        double total = 0;
        for (TransactionOutput txOutput : txOutputs) {
            total += txOutput.value;
        }
        return total;
    }

}
